﻿namespace Proto
{
    using System;
    using System.Diagnostics;
    using System.Threading;
    using Proto.Fields;
    using Proto.Providers;
    using Proto.Util;

    public class SubItem<T> : ISubItem<T> where T:class, IFieldGetter
    {
        private readonly IKey m_SubscriptionKey;
        private T m_Current;
        private T m_PendingUpdate;
        private DateTime m_LastUpdate;

        internal static int REF_COUNT = 0;
        ~SubItem()
        {
            REF_COUNT--;
        }

        internal SubItem( IKey subscription_key )
        {
            REF_COUNT++;
            m_SubscriptionKey = subscription_key;
        }

        public void Die()
        {
            Updated = delegate { };
            m_Current = null;
            m_PendingUpdate = null;
        }

        public T Current { get { return m_Current; } }
        public DateTime LastUpdate { get { return this.m_LastUpdate; } }
        public IKey SubscriptionKey{ get{ return m_SubscriptionKey; } }

        public SubState CurrentState
        {
            get
            {
                if( m_Current == null ) return SubState.Uninitialized;
                if (m_PendingUpdate == null) return SubState.Ready;
                return SubState.UpdatePending;
            }
        }


        public void EnqueueUpdate(T update)
        {
            Interlocked.Exchange(ref m_PendingUpdate, update);
            // TODO: QuickTime.Now is really slow.
            m_LastUpdate = QuickTime.Now;
        }

        public void ProcessOutstandingUpdates()
        {
            Debug.Assert(Thread.CurrentThread == RunnerRegistry.APP.Thread);

            T new_value = Interlocked.Exchange(ref m_PendingUpdate, null);
            if (new_value == null) return;
            T previous = Interlocked.Exchange(ref m_Current, new_value);

            // NOTE: Perhaps add dirty field list 
            if( Updated != null)
              Updated(this, new_value, previous);
        }

        public override string ToString()
        {
            if (m_Current != null) return "SUB FOR : " + m_Current.ToString();
            return "SUB " + CurrentState;
        }

        private event UpdateDelegate<T> m_Updated;
        public event UpdateDelegate<T> Updated;
        

        public sealed class WeakUpdateDelegate<TEventArgs>
        {
            private readonly WeakReference _targetReference;
            private readonly System.Reflection.MethodInfo _method;

            public WeakUpdateDelegate(UpdateDelegate<TEventArgs> callback)
            {
                _method = callback.Method;
                _targetReference = new WeakReference(callback.Target, true);
            }

            [DebuggerNonUserCode]

            public void Handler(ISubItem<TEventArgs> source,
                // NOTE: Do we need dirty logic?
                // FieldSet changed,
                            TEventArgs new_state,
                            TEventArgs old_state)
            {
                var target = _targetReference.Target;
                if (target != null)
                {
                    var callback = (UpdateDelegate<TEventArgs>)Delegate.CreateDelegate(typeof(UpdateDelegate<TEventArgs>), target, _method, true);
                    if (callback != null)
                    {
                        callback(source,new_state,old_state);
                    }
                }
            }
        }
    }
}